/*******************************************************************************
* Copyright (c) 2009 EclipseSource and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*   EclipseSource - initial API and implementation
*******************************************************************************/
package org.eclipse.rap.ui.interactiondesign;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.rap.ui.interactiondesign.internal.ConfigurableStackProxy;
import org.eclipse.rap.ui.internal.branding.BrandingUtil;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.internal.LayoutPart;
import org.eclipse.ui.internal.WorkbenchWindow;
import org.eclipse.ui.internal.presentations.PresentablePart;
import org.eclipse.ui.internal.util.PrefUtil;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
import org.eclipse.ui.presentations.IPresentablePart;
import org.eclipse.ui.presentations.IStackPresentationSite;
import org.eclipse.ui.presentations.StackPresentation;

/**
 * This represents an object to configure different part properties e.g.
 * toolitem visibility, viewmenu visibility or the
 * <code>{@link ConfigurableStack}</code> for a selected part.
 * <p>
 * This is a ordinary <code>{@link Action}</code>. The
 * <code>{@link #run()}</code> method can implement any configuration behaviour
 * e.g. a PupupDialog.
 * </p>
 * <p>
 * Additional this class provides methods for saving and laoding some
 * presentation specific properties.
 * </p>
 *
 * @since 1.2
 * @see Action
 * @see ConfigurableStack
 */
public abstract class ConfigurationAction extends Action {
  
  
  public ConfigurationAction() {
    allActionsVisible = allActionsVisible();
  }

  /**
   * Extracts the id of an <code>{@link ActionContributionItem}</code> from a
   * <code>{@link ToolItem}</code>.
   * @param item the <code>ToolItem</code>, which holds a
   * <code>ActionContributionItem</code>.
   *
   * @return the extracted id of an
   */
  public static final String getActionIdFromToolItem( final ToolItem item ) {
    String result = "";
    Object data = item.getData();
    ActionContributionItem toolBarAction = ( ActionContributionItem )data;
    result = toolBarAction.getId();
    return result;
  }

  private IStackPresentationSite site;
  private StackPresentation stackPresentation;
  private List configurationChangeListeners = new ArrayList();
  private boolean allActionsVisible;
  
  /**
   * Helper method to check if all view contrib items are visible by default.
   * @return true if all items are visible by default.
   */
  public static boolean allActionsVisible() {
    boolean result = false;
    String brandingId = BrandingUtil.getCurrentBrandingId();
    if( brandingId != null ) {
      IExtensionRegistry registry = Platform.getExtensionRegistry();
      final String id = "org.eclipse.rap.ui.branding";
      IExtensionPoint brandingPoint = registry.getExtensionPoint( id );
      if( brandingPoint != null ) {
        IConfigurationElement[] elements 
          = brandingPoint.getConfigurationElements();
        boolean found = false;
        for( int i = 0; i < elements.length && !found; i++ ) {
          String tempId = elements[ i ].getAttribute( "id" );
          if( tempId.equals( brandingId ) ) {
            found = true;
            IConfigurationElement[] factory 
              = elements[ i ].getChildren( "presentationFactory" );
            if( factory.length > 0 ) {
              String visibility 
                = factory[ 0 ].getAttribute( "viewActionsVisible" );              
              if( visibility != null 
                  && Boolean.valueOf( visibility ).booleanValue() ) 
              {
                result = true;
              }
            }
          }
        }
      }
    }
    return result;
  } 

  /**
   * Method to add a <code>{@link IConfigurationChangeListener}.
   * @param listener an instance of a <code>IConfigurationChangedListener
   * </code>.
   *
   * @see IConfigurationChangeListener
   */
  public void addConfigurationChangeListener(
    final IConfigurationChangeListener listener )
  {
    configurationChangeListeners.add( listener );
  }

  /**
   * This method is called if the <code>{@link ConfigurableStack}</code> of a
   * part has changed. It just calls the
   * <code>{@link IConfigurationChangeListener#presentationChanged(String)}
   * </code> method for all listeners added by
   * <code>{@link #addConfigurationChangeListener(IConfigurationChangeListener)}
   * </code>.
   *
   * @param newId the new <code>ConfigurableStack</code> id to make active.
   *
   * @see ConfigurableStack
   * @see IConfigurationChangeListener
   */
  public void fireLayoutChange( final String newId ) {
    for( int i = 0; i < configurationChangeListeners.size(); i++ ) {
      IConfigurationChangeListener listener
        = ( IConfigurationChangeListener )configurationChangeListeners.get( i );
      listener.presentationChanged( newId );

    }
  }

  /**
   * This method can be called if the configuration of the view's toolbar has
   * been changed. It calls
   * <code>{@link IConfigurationChangeListener#toolBarChanged()}</code> for all
   * listeners registered by
   * <code>{@link #addConfigurationChangeListener(IConfigurationChangeListener)}
   * </code>.
   *
   * @see IConfigurationChangeListener
   */
  public void fireToolBarChange() {
    for( int i = 0; i < configurationChangeListeners.size(); i++ ) {
      IConfigurationChangeListener listener
        = ( IConfigurationChangeListener )configurationChangeListeners.get( i );
      listener.toolBarChanged();

    }
  }

  private String getActionIdentifier( final String viewId,
                                      final String actionId )
  {
    return
        ConfigurableStackProxy.STACK_PRESENTATION_ID
      + "/"
      + viewId
      + "/" 
      + actionId;
  }

  private String getPartMenuIdentifier( final String paneId ) {
    return
        ConfigurableStackProxy.STACK_PRESENTATION_ID
      + "/"
      + paneId
      + "/partMenu";
  }

  /**
   * This method returns the <code>IStackPresentationSite</code> from the
   * <code>StackPresentation</code>, which belongs to this
   * <code>ConfigurationAction</code>.
   * @return the <code>IStackPresentationSite</code> to communicate with the
   * part.
   */
  public IStackPresentationSite getSite() {
    return site;
  }

  /**
   * Return the <code>{@link StackPresentation}</code>, which this action
   * belongs to.
   *
   * @return the <code>StackPresentation</code> object.
   *
   * @see ConfigurableStack
   */
  public StackPresentation getStackPresentation() {
    return stackPresentation;
  }

  /**
   * Checks if the selected part has a menu or not.
   *
   * @return <code>true</code> if the selected part has a menu.
   */
  public boolean hasPartMenu() {
    boolean result = false;
    IPresentablePart selectedPart = site.getSelectedPart();
    if( selectedPart instanceof PresentablePart ) {
      PresentablePart part = ( PresentablePart )selectedPart;
      result = part.getPane().hasViewMenu();
    }
    return result;
  }

  /**
   * This method is called right after the object is instantiated to set
   * different fields. These fields are necessary to get the needed
   * information about the current <code>{@link WorkbenchWindow}</code> state
   * e.g. the selected part or the current <code>{@link ConfigurableStack}
   * </code>.
   *
   * @param site the site used for communication between the presentation and
   * the workbench.
   * @param stackPresentation the current <code>{@link StackPresentation}</code>
   * or <code>ConfigurableStack</code> to get the part's toolbar and so on.
   *
   * @see ConfigurableStack
   * @see IStackPresentationSite
   * @see StackPresentation
   */
  public void init( final IStackPresentationSite site,
                    final StackPresentation stackPresentation )
  {
    this.site = site;
    this.stackPresentation = stackPresentation;
  }

  /**
   * Check if the part menu is set visible by the user.
   *
   * @return the visibility of the part menu.
   */
  public boolean isPartMenuVisible() {
    boolean result = true;
    if( !allActionsVisible) {
      if( stackPresentation instanceof ConfigurableStack ) {
        ConfigurableStack configStack = ( ConfigurableStack )stackPresentation;
        String paneId = configStack.getPaneId( site );
        String identifier = getPartMenuIdentifier( paneId );
        result = loadPartmenuVisibility( identifier );
      }
    }
    return result;
  }

  private boolean loadPartmenuVisibility( final String identifier ) {
    boolean result = false;
    IPreferenceStore preferenceStore = PrefUtil.getAPIPreferenceStore();
    result = preferenceStore.getBoolean( identifier );
    return result;
  }

  /**
   * Returns the visibility for an <code>{@link ActionContributionItem}</code>,
   * which is contributed to a view's toolbar. By default all <code>
   * ActionContributionItem</code>s are not visible. The visibility is stored
   * in a <code>ScopedPreferenceStore</code>.
   *
   * @param viewId the id of the view that holds the
   * <code>ActionContributionItem</code>.
   * @param actionId the unique id of the <code>{@link Action}</code> holding by
   * an <code>ActionContributionItem</code>.
   *
   * @return the visibility of the <code>ActionContributionItem</code>. If no
   * value is stored, the default value is <code>false</code>.
   *
   * @see ActionContributionItem
   * @see Action
   * @see ScopedPreferenceStore
   */
  public boolean isViewActionVisibile( final String viewId, 
                                       final String actionId )
  {
    boolean result = true;
    if( !allActionsVisible ) {
      String identifier = getActionIdentifier( viewId, actionId );  
      ScopedPreferenceStore prefStore
        = ( ScopedPreferenceStore ) PrefUtil.getAPIPreferenceStore();
      result = prefStore.getBoolean( identifier );
    }
    return result;
  }  

  /**
   * Removes a <code>{@link IConfigurationChangeListener}</code> from this
   * action.
   *
   * @param listener the <code>IConfigurationChangeListener</code> to remove.
   */
  public void removeLayoutChangeListener(
    final IConfigurationChangeListener listener )
  {
    boolean found = false;
    for( int i = 0; !found && i < configurationChangeListeners.size(); i++ ) {
      IConfigurationChangeListener current
        = ( IConfigurationChangeListener )configurationChangeListeners.get( i );
      if( current.equals( listener ) ) {
        configurationChangeListeners.remove( i );
        found = true;
      }
    }
  }

  /**
   * Saves the visibility of a part's menu in a
   * <code>{@link ScopedPreferenceStore}</code>.
   *
   * @param visible the new visibility to save.
   *
   * @see ScopedPreferenceStore
   */
  public void savePartMenuVisibility( final boolean visible ) {
    IPreferenceStore preferenceStore = PrefUtil.getAPIPreferenceStore();
    if( stackPresentation instanceof ConfigurableStack ) {
      ConfigurableStack configStack = ( ConfigurableStack )stackPresentation;
      String paneId = configStack.getPaneId( site );
      String identifier = getPartMenuIdentifier( paneId );
      preferenceStore.setValue( identifier, visible );
    }
  }

  /**
   * Saves the <code>{@link ConfigurableStack}</code> id for the selected
   * <code>{@link LayoutPart}</code>.
   *
   * @param id the unique id of the <code>ConfigurableStack</code> to save.
   *
   * @see LayoutPart
   * @see ConfigurableStack
   */
  public void saveStackPresentationId( final String id ) {
    if( stackPresentation instanceof ConfigurableStack ) {
      String layoutPartId = ConfigurableStack.getLayoutPartId( site );
      if( site != null && layoutPartId != null ) {
        ScopedPreferenceStore prefStore
          = ( ScopedPreferenceStore )PrefUtil.getAPIPreferenceStore();
        prefStore.setValue( ConfigurableStackProxy.STACK_PRESENTATION_ID + "/"
                            + layoutPartId,
                            id );
      }
    }
  }

  /**
   * Saves the visibility of a view action in a
   * <code>{@link ScopedPreferenceStore}</code>.
   *
   * @param viewId the id of the view, which holds the view's
   * <code>{@link ActionContributionItem}</code>
   * @param actionId the id of the <code>ActionContributionItem</code>, which
   * the new visibility belongs to.
   * @param visibility the value of the visibility.
   */
  public void saveViewActionVisibility( final String viewId,
                                        final String actionId,
                                        final boolean visibility )
  {
    String identifier = getActionIdentifier( viewId, actionId );
    ScopedPreferenceStore prefStore
      = ( ScopedPreferenceStore )PrefUtil.getAPIPreferenceStore();
    prefStore.setValue( identifier, visibility );

  }
 
}
